﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Jinndev {

    /// <summary>
    /// 多值Map，一个key对应多个value
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="V"></typeparam>
    public class MultiMap<K, V> : IReadOnlyCollection<KeyValuePair<K, List<V>>> {

        private Dictionary<K, List<V>> dictionary;
        private int valueCapacity;
        public bool removeEmptyEntry { get; private set; }

        public MultiMap(bool removeEmptyEntry = false) {
            dictionary = new Dictionary<K, List<V>>();
            this.removeEmptyEntry = removeEmptyEntry;
        }

        public MultiMap(int capacity, int valueCapacity = 0, bool removeEmptyEntry = false) {
            dictionary = new Dictionary<K, List<V>>(capacity);
            this.valueCapacity = valueCapacity;
            this.removeEmptyEntry = removeEmptyEntry;
        }

        public MultiMap(Dictionary<K, List<V>> dict, int valueCapacity = 0, bool removeEmptyEntry = false) {
            this.dictionary = dict;
            this.valueCapacity = valueCapacity;
            this.removeEmptyEntry = removeEmptyEntry;
        }
        

        public void Add(K key, V value) {
            if (!dictionary.TryGetValue(key, out List<V> list)) {
                list = valueCapacity > 0 ? new List<V>(valueCapacity) : new List<V>();
                dictionary.Add(key, list);
            }
            list.Add(value);
        }

        public void AddRange(K key, IReadOnlyList<V> values) {
            if (!dictionary.TryGetValue(key, out List<V> list)) {
                list = valueCapacity > 0 ? new List<V>(valueCapacity) : new List<V>();
                dictionary.Add(key, list);
            }
            list.AddRange(values);
        }

        public void AddRange(IReadOnlyDictionary<K, List<V>> other) {
            foreach(KeyValuePair<K, List<V>> pair in other) {
                AddRange(pair.Key, pair.Value);
            }
        }

        public void AddRange(IReadOnlyDictionary<K, V> other) {
            foreach (KeyValuePair<K, V> pair in other) {
                Add(pair.Key, pair.Value);
            }
        }

        public K[] GetKeys(IComparer<K> comparer = null) {
            if (dictionary.Count == 0) {
                return new K[0];
            }
            K[] keys = new K[dictionary.Count];
            dictionary.Keys.CopyTo(keys, 0);
            Array.Sort(keys, comparer);
            return keys;
        }

        public void SortValues() {
            foreach (List<V> list in dictionary.Values) {
                list.Sort();
            }
        }

        public void SortValues(IComparer<V> comparer) {
            foreach (List<V> list in dictionary.Values) {
                list.Sort(comparer);
            }
        }

        public void SortValues(Comparison<V> comparison) {
            foreach (List<V> list in dictionary.Values) {
                list.Sort(comparison);
            }
        }

        public Dictionary<K, List<V>>.KeyCollection Keys {
            get { return dictionary.Keys; }
        }

        public int Size(K key) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                return list.Count;
            }
            return 0;
        }

        public int Size() {
            int count = 0;
            foreach (List<V> value in dictionary.Values) {
                count += value.Count;
            }
            return count;
        }

        public int Count {
            get { return dictionary.Count; }
        }

        public List<V> GetAllValues() {
            Dictionary<K, List<V>>.ValueCollection values = dictionary.Values;
            List<V> result = new List<V>();
            foreach (List<V> list in values) {
                result.AddRange(list);
            }
            return result;
        }

        public bool GetValues(K key, List<V> list) {
            if (list != null && dictionary.TryGetValue(key, out List<V> values)) {
                list.AddRange(values);
                return true;
            }
            return false;
        }

        public List<V> GetValues(IEnumerable<K> keys) {
            List<V> result = new List<V>();
            if (keys != null) {
                foreach (K key in keys) {
                    if (dictionary.TryGetValue(key, out List<V> list)) {
                        result.AddRange(list);
                    }
                }
            }
            return result;
        }

        public bool TryGetValues(K key, out List<V> list) {
            return dictionary.TryGetValue(key, out list);
        }

        public bool TryGet(K key, int index, out V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if (index < list.Count) {
                    value = list[index];
                    return true;
                }
            }
            value = default;
            return false;
        }

        public bool TryGetFirst(K key, out V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if (list.Count > 0) {
                    value = list[0];
                    return true;
                }
            }
            value = default;
            return false;
        }

        public bool TryGetLast(K key, out V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if (list.Count > 0) {
                    value = list[list.Count - 1];
                    return true;
                }
            }
            value = default;
            return false;
        }

        public bool ContainsKey(K key) {
            return dictionary.ContainsKey(key);
        }

        public bool ContainsValue(K key, V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                return list.Contains(value);
            }
            return false;
        }

        public bool Remove(K key, V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if(list.Remove(value)) {
                    if(removeEmptyEntry && list.Count == 0) {
                        dictionary.Remove(key);
                    }
                    return true;
                }
            }
            return false;
        }

        public bool RemoveAt(K key, int index) {
            return RemoveAt(key, index, out _);
        }

        public bool RemoveFirst(K key) {
            return RemoveFirst(key, out _);
        }

        public bool RemoveLast(K key) {
            return RemoveLast(key, out _);
        }

        public bool RemoveAt(K key, int index, out V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if (index < list.Count) {
                    value = list[index];
                    list.RemoveAt(index);
                    if (removeEmptyEntry && list.Count == 0) {
                        dictionary.Remove(key);
                    }
                    return true;
                }
            }
            value = default;
            return false;
        }

        public bool RemoveFirst(K key, out V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if (list.Count > 0) {
                    value = list[0];
                    list.RemoveAt(0);
                    if (removeEmptyEntry && list.Count == 0) {
                        dictionary.Remove(key);
                    }
                    return true;
                }
            }
            value = default;
            return false;
        }

        public bool RemoveLast(K key, out V value) {
            if (dictionary.TryGetValue(key, out List<V> list)) {
                if (list.Count > 0) {
                    value = list[list.Count - 1];
                    list.RemoveAt(list.Count - 1);
                    if (removeEmptyEntry && list.Count == 0) {
                        dictionary.Remove(key);
                    }
                    return true;
                }
            }
            value = default;
            return false;
        }

        public bool RemoveAll(K key) {
            return dictionary.Remove(key);
        }

        public void Clear() {
            dictionary.Clear();
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        public IEnumerator<KeyValuePair<K, List<V>>> GetEnumerator() {
            return dictionary.GetEnumerator();
        }


    }

}